﻿using System;
using System.Windows;
using System.Windows.Markup;
using System.Windows.Media;
using System.Windows.Media.Animation;

namespace NopControls
{
    [ContentProperty("Child")]
    public partial class ExpandableTab
    {
        // Dependency properties

        public static readonly DependencyProperty ContainerBackgroundProperty =
            DependencyProperty.Register("ContainerBackground", typeof(Brush), typeof(ExpandableTab), new PropertyMetadata(new PropertyChangedCallback(OnContainerBackgroundChanged)));

        public static readonly DependencyProperty ContainerBorderBrushProperty =
            DependencyProperty.Register("ContainerBorderBrush", typeof(Brush), typeof(ExpandableTab), new PropertyMetadata(new PropertyChangedCallback(OnContainerBorderBrushChanged)));

        public static readonly DependencyProperty ChildProperty =
            DependencyProperty.Register("Child", typeof(object), typeof(ExpandableTab), new PropertyMetadata(new PropertyChangedCallback(OnChildChanged)));

        public static readonly DependencyProperty HeaderDockPositionProperty =
            DependencyProperty.Register("HeaderDockPosition", typeof(HeaderDockPosition), typeof(ExpandableTab), new PropertyMetadata(HeaderDockPosition.Top, OnHeaderDockPositionChanged));

        public static readonly DependencyProperty HeaderAlignmentProperty =
            DependencyProperty.Register("HeaderAlignment", typeof(HeaderAlignment), typeof(ExpandableTab), new PropertyMetadata(HeaderAlignment.Center, OnHeaderAlignmentChanged));

        public static readonly DependencyProperty HeaderContentProperty =
            DependencyProperty.Register("HeaderContent", typeof(object), typeof(ExpandableTab), null);

        public static readonly DependencyProperty HeaderWidthProperty =
            DependencyProperty.Register("HeaderWidth", typeof(double), typeof(ExpandableTab), null);

        public static readonly DependencyProperty HeaderHeightProperty =
            DependencyProperty.Register("HeadeHeight", typeof(double), typeof(ExpandableTab), null);

        public static readonly DependencyProperty HeaderOffsetProperty =
            DependencyProperty.Register("HeaderOffset", typeof(double), typeof(ExpandableTab), new PropertyMetadata(0.0, OnHeaderAlignmentChanged));


        /// <summary>
        /// Brush of the panel.
        /// </summary>
        public Brush ContainerBorderBrush
        {
            get { return (Brush)GetValue(ContainerBorderBrushProperty); }
            set
            {
                SetValue(ContainerBorderBrushProperty, value);
            }
        }

        static void OnContainerBorderBrushChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
        {
            ExpandableTab tab = obj as ExpandableTab;
            tab.headerBorder.BorderBrush = (Brush)args.NewValue;
            tab.border.BorderBrush = (Brush)args.NewValue;
        }

        /// <summary>
        /// Background of the panel.
        /// </summary>
        public Brush ContainerBackground
        {
            get { return (Brush)GetValue(ContainerBackgroundProperty); }
            set
            {
                SetValue(ContainerBackgroundProperty, value);
            }
        }

        static void OnContainerBackgroundChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
        {
            ExpandableTab tab = obj as ExpandableTab;
            tab.headerBorder.Background = (Brush)args.NewValue;
            tab.border.Background = (Brush)args.NewValue;
            tab.backRectangle.Fill = (Brush)args.NewValue;
        }

        /// <summary>
        /// Property used to define the content of the control
        /// </summary>
        public object Child
        {
            get { return GetValue(ChildProperty); }
            set
            {
                SetValue(ChildProperty, value);
            }
        }

        static void OnChildChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
        {
            ExpandableTab tab = obj as ExpandableTab;
            tab.content.Content = args.NewValue;
        }

        /// <summary>
        /// Where do you want to attach the header.
        /// </summary>
        public HeaderDockPosition HeaderDockPosition
        {
            get { return (HeaderDockPosition)GetValue(HeaderDockPositionProperty); }
            set
            {
                SetValue(HeaderDockPositionProperty, value);
            }
        }

        static void OnHeaderDockPositionChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
        {
            ExpandableTab tab = obj as ExpandableTab;
            tab.HandleHeaderDockPosition();
        }

        /// <summary>
        /// Alignment of the header on the ExpandableTab.
        /// </summary>
        public HeaderAlignment HeaderAlignment
        {
            get { return (HeaderAlignment)GetValue(HeaderAlignmentProperty); }
            set
            {
                SetValue(HeaderAlignmentProperty, value);
            }
        }

        static void OnHeaderAlignmentChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
        {
            ExpandableTab tab = obj as ExpandableTab;
            tab.HandleHeaderAlignment();
        }

        /// <summary>
        /// Content of the header.
        /// </summary>
        public object HeaderContent
        {
            get { return GetValue(HeaderContentProperty); }
            set
            {
                headerContent.Content = value;
                SetValue(HeaderContentProperty, value);
            }
        }

        /// <summary>
        /// Width of the header.
        /// </summary>
        public double HeaderWidth
        {
            get { return (double)GetValue(HeaderWidthProperty); }
            set
            {
                headerZone.Width = value;

                SetValue(HeaderWidthProperty, value);
            }
        }

        /// <summary>
        /// Height of the header.
        /// </summary>
        public double HeaderHeight
        {
            get { return (double)GetValue(HeaderHeightProperty); }
            set
            {
                headerZone.Height = value;

                SetValue(HeaderHeightProperty, value);
            }
        }

        /// <summary>
        /// Offset of the header.
        /// </summary>
        /// <remarks>
        /// Offset can be used with Start and End alignment.
        /// </remarks>
        public double HeaderOffset
        {
            get { return (double)GetValue(HeaderOffsetProperty); }
            set
            {
                SetValue(HeaderOffsetProperty, value);
            }
        }

        public ExpandableTab()
        {
            InitializeComponent();
        }

        private void UserControl_Loaded(object sender, RoutedEventArgs e)
        {
            backRectangle.Width = headerZone.Width - 2;
            backRectangle.Height = headerZone.Height - 2;

            HandleHeaderDockPosition();
        }

        /// <summary>
        /// Compute the alignment of the header (using the offset if required).
        /// </summary>
        void HandleHeaderAlignment()
        {
            switch (HeaderAlignment)
            {
                case HeaderAlignment.Start:
                    switch (HeaderDockPosition)
                    {
                        case HeaderDockPosition.Top:
                        case HeaderDockPosition.Bottom:
                            backRectangle.Margin = new Thickness(1 + HeaderOffset,0,0,0);
                            backRectangle.HorizontalAlignment = HorizontalAlignment.Left;
                            headerZone.Margin = new Thickness(HeaderOffset, 0, 0, 0);
                            headerZone.HorizontalAlignment = HorizontalAlignment.Left;
                            break;
                        case HeaderDockPosition.Left:
                        case HeaderDockPosition.Right:
                            backRectangle.Margin = new Thickness(0, 1 + HeaderOffset, 0, 0);
                            backRectangle.VerticalAlignment = VerticalAlignment.Top;
                            headerZone.Margin = new Thickness(0, HeaderOffset, 0, 0);
                            headerZone.VerticalAlignment = VerticalAlignment.Top;
                            break;
                    }
                    break;
                case HeaderAlignment.Center:
                    switch (HeaderDockPosition)
                    {
                        case HeaderDockPosition.Top:
                        case HeaderDockPosition.Bottom:
                            backRectangle.HorizontalAlignment = HorizontalAlignment.Center;
                            headerZone.HorizontalAlignment = HorizontalAlignment.Center;
                            break;
                        case HeaderDockPosition.Left:
                        case HeaderDockPosition.Right:
                            backRectangle.VerticalAlignment = VerticalAlignment.Center;
                            headerZone.VerticalAlignment = VerticalAlignment.Center;
                            break;
                    }
                    break;
                case HeaderAlignment.End:
                    switch (HeaderDockPosition)
                    {
                        case HeaderDockPosition.Top:
                        case HeaderDockPosition.Bottom:
                            backRectangle.Margin = new Thickness(0, 0, 1 + HeaderOffset, 0);
                            backRectangle.HorizontalAlignment = HorizontalAlignment.Right;
                            headerZone.Margin = new Thickness(0, 0, HeaderOffset, 0);
                            headerZone.HorizontalAlignment = HorizontalAlignment.Right;
                            break;
                        case HeaderDockPosition.Left:
                        case HeaderDockPosition.Right:
                            backRectangle.Margin = new Thickness(0, 0, 0, 1 + HeaderOffset);
                            backRectangle.VerticalAlignment = VerticalAlignment.Bottom;
                            headerZone.Margin = new Thickness(0, 0, 0, HeaderOffset);
                            headerZone.VerticalAlignment = VerticalAlignment.Bottom;
                            break;
                    }
                    break;
            }           
        }

        /// <summary>
        /// Define the behavoir of the inner controls according to header's position.
        /// </summary>
        void HandleHeaderDockPosition()
        {
            RectangleGeometry rect = new RectangleGeometry();

            switch (HeaderDockPosition)
            {
                case HeaderDockPosition.Top:
#if WINDOWS_PHONE
                    rect.Rect = new Rect(-4, 4, ActualWidth + 4, ActualHeight + headerZone.ActualHeight + 4);
#else
                    rect.Rect = new Rect(-4, 0, ActualWidth + 4, ActualHeight + headerZone.ActualHeight + 4);
#endif

                    globalTransform.Y = -border.ActualHeight;
                    headerZone.VerticalAlignment = VerticalAlignment.Bottom;
                    backRectangle.VerticalAlignment = VerticalAlignment.Bottom;
                    headerTransform.Y = headerZone.ActualHeight - 3;
                    backRectangleTransform.Y = 2;

                    VerticalAlignment = VerticalAlignment.Top;
                    break;
                case HeaderDockPosition.Left:
                    rect.Rect = new Rect(0, -4, ActualWidth + headerZone.ActualWidth + 4, ActualHeight + 4);

                    globalTransform.X = -ActualWidth;
                    headerZone.HorizontalAlignment = HorizontalAlignment.Right;
                    backRectangle.HorizontalAlignment = HorizontalAlignment.Right;
                    headerTransform.X = headerZone.ActualWidth - 3;
                    backRectangleTransform.X = 2;

                    HorizontalAlignment = HorizontalAlignment.Left;
                    break;
                case HeaderDockPosition.Right:
                    rect.Rect = new Rect(-(headerZone.ActualWidth + 4), -4, ActualWidth + headerZone.ActualWidth + 4, ActualHeight + 4);

                    globalTransform.X = ActualWidth;
                    headerZone.HorizontalAlignment = HorizontalAlignment.Left;
                    backRectangle.HorizontalAlignment = HorizontalAlignment.Left;
                    headerTransform.X = -headerZone.ActualWidth + 3;
                    backRectangleTransform.X = -2;

                    HorizontalAlignment = HorizontalAlignment.Right;
                    break;
                case HeaderDockPosition.Bottom:
#if WINDOWS_PHONE
                    rect.Rect = new Rect(-4, -(headerZone.ActualHeight + 4), ActualWidth + 4, ActualHeight + headerZone.ActualHeight + 1);
#else
                    rect.Rect = new Rect(-4, -(headerZone.ActualHeight + 4), ActualWidth + 4, ActualHeight + headerZone.ActualHeight + 4);
#endif
                    globalTransform.Y = ActualHeight;
                    headerZone.VerticalAlignment = VerticalAlignment.Top;
                    backRectangle.VerticalAlignment = VerticalAlignment.Top;
                    headerTransform.Y = -headerZone.ActualHeight + 3;
                    backRectangleTransform.Y = -2;
                    VerticalAlignment = VerticalAlignment.Bottom;
                    break;
            }

            Clip = rect;
        }

        // Mouse clic and mouse over

        private void headerZone_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            Storyboard sb = new Storyboard();
            string path = "";
            double to = 0;

            switch (HeaderDockPosition)
            {
                case HeaderDockPosition.Top:
                    to = globalTransform.Y == 0 ? -ActualHeight : 0;
                    path = "Y";
                    break;
                case HeaderDockPosition.Left:
                    to = globalTransform.X == 0 ? -ActualWidth : 0;
                    path = "X";
                    break;
                case HeaderDockPosition.Right:
                    to = globalTransform.X == 0 ? ActualWidth : 0;
                    path = "X";
                    break;
                case HeaderDockPosition.Bottom:
                    to = globalTransform.Y == 0 ? ActualHeight : 0;
                    path = "Y";
                    break;
            }

            DoubleAnimation animation = new DoubleAnimation
            {
                To = to,
                Duration =
                    TimeSpan.FromMilliseconds(300)
            };
            BackEase ease = new BackEase
            {
                EasingMode = EasingMode.EaseInOut,
                Amplitude = 0.6
            };
            animation.EasingFunction = ease;

            Storyboard.SetTarget(animation, globalTransform);
            Storyboard.SetTargetProperty(animation, new PropertyPath(path));

            sb.Children.Add(animation);
            sb.Begin();
        }

        private void headerZone_MouseEnter(object sender, System.Windows.Input.MouseEventArgs e)
        {
            mouseOverStoryboard.Begin();
        }

        private void headerZone_MouseLeave(object sender, System.Windows.Input.MouseEventArgs e)
        {
            mouseOutStoryboard.Begin();
        }
    }
}
